package com.benshou.bsoa.common.dao;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.benshou.bsoa.common.dao.params.QueryParams;
import com.benshou.bsoa.common.db.BaseMapper;
import com.benshou.bsoa.common.db.BaseModel;
import com.benshou.bsoa.common.util.Asserts;
import jakarta.annotation.Nullable;
import lombok.NonNull;
import org.apache.ibatis.session.ExecutorType;
import org.springframework.transaction.annotation.Transactional;

import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.function.BiPredicate;
import java.util.function.Predicate;
import java.util.function.Supplier;

/**
 * 基于 BaseModel 的 DAO 实现
 *
 * @author <a href="mailto:liuzhenbang@benshou.com">Liu Zhenbang</a>
 */
public abstract class AbstractBaseModelDaoImpl<Mapper extends BaseMapper<T>, T extends BaseModel>
        extends ServiceImpl<Mapper, T> {

    /**
     * 获取空白的数据对象（用于生成数据更新对象）
     *
     * @return 空白的数据对象
     */
    protected abstract T getEmptyModel();

    /**
     * 根据参数，对符合条件的数据执行删除（逻辑删除）
     *
     * @param params 参数对象
     * @return 是否删除成功
     */
    public boolean delete(@NonNull QueryParams<T> params) {
        return update(params, () -> {
            var model = getEmptyModel();
            model.setRecordInd(BaseModel.RecordIndEnum.DELETED.getValue());
            return model;
        });
    }

    /**
     * 根据参数，查询获取符合条件的唯一的一条数据
     *
     * @param params 参数对象
     * @return 查询到的数据对象
     */
    public @Nullable T getOne(@NonNull QueryParams<T> params) {
        return getOne(
                params.applyParams(lambdaQuery())
                        .eq(T::getRecordInd, BaseModel.RecordIndEnum.ADDED.getValue())
        );
    }

    /**
     * 根据参数，查询获取符合条件的数据列表
     *
     * @param params 参数对象
     * @return 查询到的数据列表
     */
    public List<T> list(QueryParams<T> params) {
        var query = lambdaQuery();
        Optional.ofNullable(params)
                .ifPresent(it -> it.applyParams(query));
        return list(
                query.eq(T::getRecordInd, BaseModel.RecordIndEnum.ADDED.getValue())
        );
    }

    /**
     * 根据参数，分页查询获取符合条件的数据列表
     *
     * @param pageNo 当前页码
     * @param pageSize 页数据条数
     * @param params 参数对象
     * @return 查询到的分页数据对象
     */
    public Page<T> getPage(long pageNo, long pageSize, QueryParams<T> params) {
        var query = lambdaQuery();
        Optional.ofNullable(params)
                .ifPresent(it -> it.applyParams(query));
        return page(
                Page.of(pageNo, pageSize),
                query.eq(T::getRecordInd, BaseModel.RecordIndEnum.ADDED.getValue())
        );
    }

    /**
     * 根据参数，分页查询获取数据列表（简化版，不设置额外的查询条件）
     *
     * @param pageNo 当前页码
     * @param pageSize 页数据条数
     * @return 查询到的分页数据对象
     */
    public Page<T> getPage(long pageNo, long pageSize) {
        return getPage(pageNo, pageSize, null);
    }

    /**
     * 根据参数，将 Model 中的数据，更新到符合条件的数据
     *
     * @param params 参数
     * @param updateModelSupplier Model提供方法
     * @return 是否更新成功
     */
    public boolean update(@NonNull QueryParams<T> params, @NonNull Supplier<T> updateModelSupplier) {
        return update(
                updateModelSupplier.get(),
                params.applyParams(lambdaUpdate())
                        .eq(T::getRecordInd, BaseModel.RecordIndEnum.ADDED.getValue())
        );
    }



    /**
     * 执行批量操作
     * @param dataCollection 要处理的数据集合
     * @param batchOperationPredicate 批量执行的逻辑，接收集合和Mapper作为参数，返回执行结果
     * @param <C> 集合类型
     * @return 批量操作是否成功
     */
    public <C extends Collection<?>> boolean executeBatchOperation(
            @NonNull C dataCollection,
            @NonNull BiPredicate<C, Mapper> batchOperationPredicate
    ) {
        // 空集合直接返回成功
        if (dataCollection.isEmpty()) {
            return true;
        }

        // 使用BATCH类型的SqlSession以提高性能
        try (var sqlSession = getSqlSessionFactory().openSession(ExecutorType.BATCH)) {
            // 获取Mapper接口实例
            var mapper = sqlSession.getMapper(getMapperClass());
            // 执行批量操作
            boolean operationSuccess;
            try {
                operationSuccess = batchOperationPredicate.test(dataCollection, mapper);
            } catch (Exception e) {
                // 出现异常则回滚事务
                sqlSession.rollback();
                throw e;
            }

            if (operationSuccess) {
                // 操作成功则提交事务
                sqlSession.commit();
            } else {
                // 操作失败则回滚事务
                sqlSession.rollback();
            }

            return operationSuccess;
        }
    }

    /**
     * 批量对数据进行新增或更新
     *
     * @param saveCollection 待新增的数据列表
     * @param saveOperationPredicate 新增方法以及是否新增成功的结果返回
     * @param updateCollection 待更新的数据列表
     * @param updateOperationPredicate 更新方法以及是否更新成功的结果返回
     * @param <TT> 待新增或更新的数据类型
     * @param <S> 待新增的列表类型
     * @param <U> 待更新的列表类型
     * @throws RuntimeException 若新增/更新的过程中执行失败，抛出此异常，用于数据回滚
     */
    @Transactional(rollbackFor = Exception.class)
    public <TT, S extends Collection<? extends TT>, U extends Collection<? extends TT>> void saveOrUpdateBatch(
            S saveCollection,
            @NonNull Predicate<? super S> saveOperationPredicate,
            U updateCollection,
            @NonNull Predicate<? super U> updateOperationPredicate
    ) throws RuntimeException {
        Optional.ofNullable(saveCollection)
                .filter(it -> !it.isEmpty())
                .ifPresent(
                        it -> Asserts.isTrue(
                                saveOperationPredicate.test(it),
                                RuntimeException::new
                        )
                );
        Optional.ofNullable(updateCollection)
                .filter(it -> !it.isEmpty())
                .ifPresent(
                        it -> Asserts.isTrue(
                                updateOperationPredicate.test(it),
                                RuntimeException::new
                        )
                );
    }
}
